﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using XrmLibrary.Exceptions;
using XrmLibrary.Helpers;

namespace XrmLibrary.MsCrm4Adapter.Extensions
{
    public static class KeyValuePairExtensions
    {
        public static Property ToCrm4Property(this KeyValuePair<String, Object> attribute, String primaryKey)
        {
            Property property = null;
            TypeSwitch.Do(attribute.Value,
                TypeSwitch.Case<Boolean>(() => property = attribute.ToCrm4BooleanProperty()),
                TypeSwitch.Case<DateTime>(() => property = attribute.ToCrm4DateTimeProperty()),
                TypeSwitch.Case<Decimal>(() => property = attribute.ToCrm4DecimalProperty()),
                TypeSwitch.Case<Double>(() => property = attribute.ToCrm4FloatProperty()),
                TypeSwitch.Case<Microsoft.Xrm.Sdk.Money>(() => property = attribute.ToCrm4MoneyProperty()),
                TypeSwitch.Case<Int32>(() => property = attribute.ToCrm4NumberProperty()),
                TypeSwitch.Case<Microsoft.Xrm.Sdk.EntityReference>(() => property = GetPropertyForEntityReferenceAttribute(attribute)),
                TypeSwitch.Case<Microsoft.Xrm.Sdk.EntityCollection>(() => property = attribute.ToCrm4DynamicEntityArrayProperty()),
                TypeSwitch.Case<Guid>(() => property = GetPropertyForGuidAttribute(attribute, primaryKey)),
                TypeSwitch.Case<Microsoft.Xrm.Sdk.OptionSetValue>(() => property = GetPropertyForOptionSetValueAttribute(attribute)),
                TypeSwitch.Case<String>(() => property = attribute.ToCrm4StringProperty()),
                TypeSwitch.Default(() => property = attribute.ToCrm4DefaultProperty()));
            ValidateProperty(property, attribute);
            return property;
        }


        private static Property GetPropertyForEntityReferenceAttribute(KeyValuePair<String, Object> attribute)
        {
            Property property = null;
            switch(attribute.Key)
            {
                case "parentcustomerid":
                case "customerid":
                    property = attribute.ToCrm4CustomerProperty();
                    break;
                case "ownerid":
                    property = attribute.ToCrm4OwnerProperty();
                    break;
                default:
                    property = attribute.ToCrm4LookupProperty();
                    break;
            }
            return property;
        }

        private static Property GetPropertyForGuidAttribute(KeyValuePair<String, Object> attribute, String primaryKey)
        {
            Property property = null;
            if(attribute.Key == primaryKey)
            {
                property = attribute.ToCrm4KeyProperty();
            }
            else
            {
                property = attribute.ToCrm4UniqueIdentifierProperty();
            }
            return property;
        }

        private static Property GetPropertyForOptionSetValueAttribute(KeyValuePair<String, Object> attribute)
        {
            Property property = null;
            switch(attribute.Key)
            {
                case "statecode":
                    property = attribute.ToCrm4StateProperty();
                    break;
                case "statuscode":
                    property = attribute.ToCrm4StatusProperty();
                    break;
                default:
                    property = attribute.ToCrm4PicklistProperty();
                    break;
            }
            return property;
        }

        private static void ValidateProperty(Property property, KeyValuePair<String, Object> attribute)
        {
            if(property == null)
            {
                String errorMessage = String.Format("The attribute '{0}' with type '{1}' does not have a supported CRM 4 conversion.", attribute.Key ?? "NULL", attribute.Value.GetType());
                throw new XrmLibraryException(errorMessage);
            }
        }

        public static Property ToCrm4BooleanProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Boolean value = (Boolean) attribute.Value;
            CrmBooleanProperty property = new CrmBooleanProperty(key, new CrmBoolean(value));
            return property;
        }

        public static Property ToCrm4DateTimeProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            DateTime value = (DateTime) attribute.Value;
            CrmDateTimeProperty property = new CrmDateTimeProperty(key, value.ToCrmDateTime());
            return property;
        }

        public static Property ToCrm4DecimalProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Decimal value = (Decimal) attribute.Value;
            CrmDecimalProperty property = new CrmDecimalProperty(key, new CrmDecimal(value));
            return property;
        }

        public static Property ToCrm4FloatProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Double value = (Double) attribute.Value;
            CrmFloatProperty property = new CrmFloatProperty(key, new CrmFloat(value));
            return property;
        }

        public static Property ToCrm4MoneyProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.Money value = (Microsoft.Xrm.Sdk.Money) attribute.Value;
            CrmMoneyProperty property = new CrmMoneyProperty(key, new CrmMoney(value.Value));
            return property;
        }

        public static Property ToCrm4NumberProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Int32 value = (Int32) attribute.Value;
            CrmNumberProperty property = new CrmNumberProperty(key, new CrmNumber(value));
            return property;
        }
        
        public static Property ToCrm4CustomerProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.EntityReference value = (Microsoft.Xrm.Sdk.EntityReference) attribute.Value;
            CustomerProperty property = new CustomerProperty(key, new Customer(value.LogicalName, value.Id));
            return property;
        }

        public static Property ToCrm4DynamicEntityArrayProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.EntityCollection value = (Microsoft.Xrm.Sdk.EntityCollection) attribute.Value;
            DynamicEntityArrayProperty property = new DynamicEntityArrayProperty(key, value.ToDynamicEntitySet().ToArray());
            return property;
        }

        public static Property ToCrm4EntityNameReferenceProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            String value = (String)attribute.Value;
            EntityNameReferenceProperty property = new EntityNameReferenceProperty(key, new EntityNameReference(value));
            return property;
        }

        public static Property ToCrm4KeyProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Guid value = (Guid) attribute.Value;
            KeyProperty property = new KeyProperty(key, new Key(value));
            return property;
        }

        public static Property ToCrm4LookupProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.EntityReference value = (Microsoft.Xrm.Sdk.EntityReference) attribute.Value;
            LookupProperty property = new LookupProperty(key, new Lookup(value.LogicalName, value.Id));
            return property;
        }

        public static Property ToCrm4OwnerProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.EntityReference value = (Microsoft.Xrm.Sdk.EntityReference) attribute.Value;
            OwnerProperty property = new OwnerProperty(key, new Owner(value.LogicalName, value.Id));
            return property;
        }

        public static Property ToCrm4PicklistProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.OptionSetValue value = (Microsoft.Xrm.Sdk.OptionSetValue) attribute.Value;
            PicklistProperty property = new PicklistProperty(key, new Picklist(value.Value));
            return property;
        }

        public static Property ToCrm4StateProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            StateProperty property;
            if (attribute.Value is Microsoft.Xrm.Sdk.OptionSetValue)
            {
                Microsoft.Xrm.Sdk.OptionSetValue value = (Microsoft.Xrm.Sdk.OptionSetValue) attribute.Value;
                // TODO: May need to retrieve the option sets display value. (use extension methods from XrmLibrary.Extensions
                property = new StateProperty(key, value.Value.ToString());
            }
            else
            {
                String value = (String) attribute.Value;
                property = new StateProperty(key, value);
            }
            return property;
        }

        public static Property ToCrm4StatusProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Microsoft.Xrm.Sdk.OptionSetValue value = (Microsoft.Xrm.Sdk.OptionSetValue) attribute.Value;
            StatusProperty property = new StatusProperty(key, new Status(value.Value));
            return property;
        }

        public static Property ToCrm4StringProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            String value = (String) attribute.Value;
            StringProperty property = new StringProperty(key, value);
            return property;
        }

        public static Property ToCrm4UniqueIdentifierProperty(this KeyValuePair<String, Object> attribute)
        {
            String key = attribute.Key;
            Guid value = (Guid) attribute.Value;
            UniqueIdentifierProperty property = new UniqueIdentifierProperty(key, new UniqueIdentifier(value));
            return property;
        }

        public static Property ToCrm4DefaultProperty(this KeyValuePair<String, Object> attribute)
        {
            return attribute.ToCrm4StringProperty();
        }
    }
}
